package views;

import java.awt.Font;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;

import javax.media.opengl.GL;

import models.AttractorGen;
import models.Vector3;
import processing.core.PApplet;
import processing.opengl.PGraphicsOpenGL;

import com.sun.opengl.util.BufferUtil;
import com.sun.opengl.util.j2d.TextRenderer;

import controllers.MainController;

public class Canvas extends PApplet
{
	/**
	 * Generated serial ID
	 */
	private static final long	serialVersionUID	= 4259767900446651940L;
	
	/**
	 * GL STUFF
	 */

	private PGraphicsOpenGL		pgl					= null;
	private GL					gl					= null;

	private int[]				listVBO				= null;

	public static DoubleBuffer	doubleBuffer		= null;
	public static ByteBuffer	byteBuffer			= null;

	private TextRenderer		textRenderer		= null;

	/**
	 * ################### # Constants # ###################
	 */

	public static final int		WIDTH				= 792;
	public static final int		HEIGHT				= 720;
	
	/**
	 * ################# # Rest of vars # ##################
	 */
	
	private MainController		mainController			= null;
	
	private Vector3				position			= null;
	private Vector3				rotation			= null;

	private long				start				= 0;
	private long				end					= 0;
	private long				calcTime			= 0;
	private long				drawTime			= 0;
	private long				timer				= 0;
	private long				timerStart			= 0;
	private long				timerEnd			= 0;
	private int					currentframe		= 0;
	private int					fps					= 0;
	
	public static boolean		PAUSE				= false;
	public static boolean		DRAW				= false;
	public static boolean		CALC				= false;
	public static boolean 		PICKOVER			= true;
	public static boolean 		DEJONG				= false;
	public static boolean 		CUSTOM				= false;
	public static boolean 		BW					= false;
	public static boolean 		COLOR				= true;
	public static boolean		SHOW_DEBUG			= false;
	

	/**
	 * ################## # End of vars # ##################
	 * @param mainController 
	 */
	
	public Canvas(MainController mainController)
	{
		this.mainController = mainController;
	}
	
	public void setup()
	{		
		size(WIDTH, HEIGHT, OPENGL);
		colorMode(HSB, 1, 1, 1);
		
		position = new Vector3();
		rotation = new Vector3();
		
		pgl = (PGraphicsOpenGL) g;
		gl = pgl.gl;
		
		listVBO = new int[2];
		
		doubleBuffer = BufferUtil.newDoubleBuffer(AttractorGen.MAX_NR_OF_PTS * 3);
		byteBuffer = BufferUtil.newByteBuffer(AttractorGen.MAX_NR_OF_PTS * 3);
		
		Font font = new Font("SansSerif", Font.BOLD, 12);
		textRenderer = new TextRenderer(font, true, true);

		pgl.beginGL();
		{
			gl.glDepthMask(false);
			
			gl.glClearColor(0.f, 0.f, 0.f, 0.f);
			
			gl.glGenBuffers(2, listVBO, 0);
		}
		pgl.endGL();
	}
	
	public void update()
	{
		timerStart = System.currentTimeMillis();

		if (keyPressed && key == CODED && keyCode == CONTROL)
			position.z -= 15;
		
		if (keyPressed && key == ' ')
			position.z += 15;
		
		if (keyPressed && key == 'a')
			position.x += 15;
		
		if (keyPressed && key == 'd')
			position.x -= 15;
		
		if (keyPressed && key == 'w')
			position.y += 15;
		
		if (keyPressed && key == 's')
			position.y -= 15;
		
		if (keyPressed && key == 'q')
			rotation.y -= 5;
		
		if (keyPressed && key == 'e')
			rotation.y += 5;

		if (CALC)
		{
			if (!PAUSE)
			{
				start = System.currentTimeMillis();
				{
					if (Canvas.PICKOVER)
						mainController.getAG().calcPickover();
					else if (Canvas.DEJONG)
						mainController.getAG().calcDeJong();
					else if (Canvas.CUSTOM)
						mainController.getAG().calc();
//						mainController.getAG().calcJS();
				}
				end = System.currentTimeMillis();
				calcTime = end - start;
				
				AttractorGen.A += AttractorGen.STEP;
				AttractorGen.B += AttractorGen.STEP;
				AttractorGen.C += AttractorGen.STEP;
				AttractorGen.D += AttractorGen.STEP;
				
				if (Canvas.CUSTOM)
				{
					mainController.getAG().getEqParser().getVarNode("A").setValue(AttractorGen.A);
					mainController.getAG().getEqParser().getVarNode("B").setValue(AttractorGen.B);
					mainController.getAG().getEqParser().getVarNode("C").setValue(AttractorGen.C);
					mainController.getAG().getEqParser().getVarNode("D").setValue(AttractorGen.D);
					mainController.getAG().getEqParser().getVarNode("E").setValue(AttractorGen.E);
					
					mainController.getAG().updateVars(AttractorGen.STEP);
					
//					mainController.getAG().getJsEngine().put("A", AttractorGen.A);
//					mainController.getAG().getJsEngine().put("B", AttractorGen.B);
//					mainController.getAG().getJsEngine().put("C", AttractorGen.C);
//					mainController.getAG().getJsEngine().put("D", AttractorGen.D);
//					mainController.getAG().getJsEngine().put("E", AttractorGen.E);
				}
			}
		}
		else
		{
			doubleBuffer.clear();
			byteBuffer.clear();
		}
	}

	public void draw()
	{
		update();

		start = System.currentTimeMillis();
		{
			if (CALC)
			{
				drawPoints();
				
				if (keyPressed && key == 'b') 
		            filter(BLUR, 5.5f);
			}
			else
				background(0);
		}
		end = System.currentTimeMillis();
		drawTime = end - start;
		
		timerEnd = System.currentTimeMillis();
		
		timer += timerEnd - timerStart;
		
		if (timer >= 1000)
		{
			timer = 0;
			fps = currentframe;
			currentframe = 0;
		}
		
		currentframe++;
	}
	
	void drawPoints()
	{
		// Set the proper position in buffers
		doubleBuffer.rewind();
		byteBuffer.rewind();
		
		pgl.beginGL();
		
		gl.glTranslatef(width / 2, height / 2, 0);
		
		gl.glTranslatef((float)position.x, (float)position.y, (float)position.z);
		gl.glRotatef((float)rotation.y, 1.f, 0.f, 0.f);

		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_COLOR);
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); // nice perspective correction
		
		gl.glEnable(GL.GL_POINT_SMOOTH);
		gl.glPointSize(2.f);
		
		gl.glBindBuffer(GL.GL_ARRAY_BUFFER_ARB, listVBO[0]);
		gl.glBufferData(GL.GL_ARRAY_BUFFER_ARB, AttractorGen.NR_OF_PTS * 3
						* BufferUtil.SIZEOF_DOUBLE, doubleBuffer,
						GL.GL_DYNAMIC_DRAW_ARB);

		gl.glBindBuffer(GL.GL_ARRAY_BUFFER_ARB, listVBO[1]);
		gl.glBufferData(GL.GL_ARRAY_BUFFER_ARB, AttractorGen.NR_OF_PTS * 3
						* BufferUtil.SIZEOF_BYTE, byteBuffer,
						GL.GL_DYNAMIC_DRAW_ARB);

		gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL.GL_COLOR_ARRAY);

		gl.glBindBuffer(GL.GL_ARRAY_BUFFER_ARB, listVBO[0]);
		gl.glVertexPointer(3, GL.GL_DOUBLE, 0, 0);

		gl.glBindBuffer(GL.GL_ARRAY_BUFFER_ARB, listVBO[1]);
		gl.glColorPointer(3, GL.GL_UNSIGNED_BYTE, 0, 0);

		gl.glDrawArrays(GL.GL_POINTS, 0, AttractorGen.NR_OF_PTS);

		gl.glDisableClientState(GL.GL_COLOR_ARRAY);
		gl.glDisableClientState(GL.GL_VERTEX_ARRAY);

		// gl.glFinish();

		pgl.endGL();

		doubleBuffer.clear();
		byteBuffer.clear();
		
		if (SHOW_DEBUG)
		{
			textRenderer.beginRendering(WIDTH, HEIGHT);
			textRenderer.setColor(0.8f, 0.2f, 0.2f, 1.0f);
			textRenderer.draw("NR_OF_PTS: " + AttractorGen.NR_OF_PTS, 10, HEIGHT - 20);
			textRenderer.draw("calcTime: " + calcTime + "ms", 10, HEIGHT - 40);
			textRenderer.draw("drawTime: " + drawTime + "ms", 10, HEIGHT - 60);
			textRenderer.draw("FPS: " + fps, 10, HEIGHT - 80);
			textRenderer.draw("Free memory: " + Runtime.getRuntime().freeMemory() / 1024, 10, HEIGHT - 100);
			textRenderer.draw("Step: " + AttractorGen.STEP, 10, HEIGHT - 120);
			textRenderer.endRendering();
		}
	}
	
	public void keyReleased()
	{
		if (key == 'p')
			Canvas.PAUSE = !Canvas.PAUSE;
	}
}
